package com.it.utils;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.DynaBean;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.github.luben.zstd.Zstd;
import com.it.annotation.Excel;
import com.it.annotation.ExcelProperty;
import com.it.bean.core.SuperBean;
import com.it.vo.MenuVo;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.UserAgent;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.springframework.http.HttpHeaders;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;

public class AppUtils {
    public static final byte[] AES_KEYS = "20250522tang&&$$".getBytes(StandardCharsets.UTF_8);

    //加密用户密码
    public static String md5(String str) {
        for (int i = 0; i < 1024; i++) {
            str = DigestUtil.md5Hex(str);
        }
        return str;
    }

    //构建菜单树
    public static List<MenuVo> buildMenuTree(List<MenuVo> menuVos) {
        // 1. 用Map加速节点查找
        Map<Long, MenuVo> nodeMap = new HashMap<>();
        for (MenuVo node : menuVos) {
            nodeMap.put(node.getId(), node);
        }
        // 2. 构建树结构
        List<MenuVo> tree = new ArrayList<>();
        for (MenuVo node : menuVos) {
            Long pid = node.getPid();
            if (pid == 0) {
                tree.add(node);
            } else {
                MenuVo parent = nodeMap.get(pid);
                if (parent != null) {
                    if (parent.getChildren() == null) {
                        List<MenuVo> child = new ArrayList<>();
                        child.add(node);
                        parent.setChildren(child);
                    } else {
                        parent.getChildren().add(node);
                    }
                }
            }
        }
        return tree;
    }

    //审计数据
    public static <T extends SuperBean> void forSaveAudit(T superBean) {
        DynaBean bean = BeanUtil.createDynaBean(superBean);
        bean.set("createTime", new Date());
        bean.set("createUser", auditUserInfo());
        bean.set("deleted", false);
    }

    public static <T extends SuperBean> void forSaveAudit(List<T> superBeans) {
        if (Objects.nonNull(superBeans)) {
            for (T superBean : superBeans) {
                forSaveAudit(superBean);
            }
        }
    }

    public static <T extends SuperBean> void forUpdateAudit(T superBean) {
        DynaBean bean = BeanUtil.createDynaBean(superBean);
        bean.set("updateTime", new Date());
        bean.set("updateUser", auditUserInfo());
    }

    public static <T extends SuperBean> void forUpdateAudit(List<T> superBeans) {
        if (Objects.nonNull(superBeans)) {
            for (T superBean : superBeans) {
                forUpdateAudit(superBean);
            }
        }
    }

    //获取登陆者信息
    public static String auditUserInfo() {
        String auditUsername = null;
        try {
            String loginId = StpUtil.getLoginIdAsString();
            SaSession session = StpUtil.getTokenSession();
            String username = (String) session.get("username");
            auditUsername = username + "@" + loginId;
        } catch (Exception e) {
            auditUsername = "system";
        }
        return auditUsername;
    }


    public static String encryption(String data) {
        return SecureUtil.aes(AES_KEYS).encryptHex(data);
    }

    public static String decryption(String data) {
        return SecureUtil.aes(AES_KEYS).decryptStr(data);
    }

    public static HttpHeaders downloadFileHeaders(String agent, String fileName) throws UnsupportedEncodingException {
        HttpHeaders headers = new HttpHeaders();
        UserAgent userAgent = UserAgent.parseUserAgentString(agent);
        String contentDisposition;
        if (userAgent.getBrowser() == Browser.FIREFOX) {
            contentDisposition = String.format("attachment;filename*=utf-8'zh_cn'%s", URLEncoder.encode(fileName, "utf-8"));
        } else {
            contentDisposition = String.format("attachment;filename=%s", URLEncoder.encode(fileName, "utf-8"));
        }
        headers.add(CONTENT_DISPOSITION, contentDisposition);
        headers.add(CONTENT_TYPE, "application/octet-stream");
        return headers;
    }

    public static void setDownloadFileHeaders(String agent, String fileName, HttpServletResponse response) throws UnsupportedEncodingException {
        UserAgent userAgent = UserAgent.parseUserAgentString(agent);
        String contentDisposition;
        if (userAgent.getBrowser() == Browser.FIREFOX) {
            contentDisposition = String.format("attachment;filename*=utf-8'zh_cn'%s", URLEncoder.encode(fileName, "utf-8"));
        } else {
            contentDisposition = String.format("attachment;filename=%s", URLEncoder.encode(fileName, "utf-8"));
        }
        response.setHeader(CONTENT_DISPOSITION, contentDisposition);
        response.setContentType("application/octet-stream");
    }

    public static Map<String, String> exportAliasMap(Class<?> type) {
        Field[] fields = ReflectUtil.getFields(type);
        List<Field> fieldList = Arrays.asList(fields);
        return fieldList.stream()
                .filter(x -> {
                    ExcelProperty excelProperty = x.getAnnotation(ExcelProperty.class);
                    return Objects.nonNull(excelProperty) && excelProperty.isExport();
                })
                .sorted(Comparator.comparing(x -> {
                    ExcelProperty excelProperty = x.getAnnotation(ExcelProperty.class);
                    return excelProperty.index();
                }))
                .collect(Collectors.toMap(Field::getName,
                        field -> {
                            ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                            return excelProperty.name();
                        },
                        (exist, newValue) -> newValue,
                        LinkedHashMap::new));
    }

    public static Map<String, String> importAliasMap(Class<?> type) {
        Field[] fields = ReflectUtil.getFields(type);
        List<Field> fieldList = Arrays.asList(fields);
        return fieldList.stream()
                .filter(x -> {
                    ExcelProperty excelProperty = x.getAnnotation(ExcelProperty.class);
                    return Objects.nonNull(excelProperty) && excelProperty.isImport();
                })
                .sorted(Comparator.comparing(x -> {
                    ExcelProperty excelProperty = x.getAnnotation(ExcelProperty.class);
                    return excelProperty.index();
                }))
                .collect(Collectors.toMap(
                        field -> {
                            ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                            return excelProperty.name();
                        },
                        Field::getName,
                        (exist, newValue) -> newValue,
                        LinkedHashMap::new));
    }

    public static void setHeaderStyle(ExcelWriter writer) {
        CellStyle headCellStyle = writer.getHeadCellStyle();
        headCellStyle.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex());
        headCellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        headCellStyle.setFont(new XSSFFont());
        Sheet sheet = writer.getSheet();
        sheet.setDefaultColumnWidth(35);
    }

    public static String getExcelName(Class<?> type) {
        Excel excel = type.getAnnotation(Excel.class);
        return excel.value() + ".xlsx";
    }

    public static byte[] compress(byte[] data) throws IOException {
        if (data == null || data.length == 0) {
            return data;
        }
        try {
            return Zstd.compress(data);
        } catch (Exception e) {
            throw new IOException("压缩数据失败", e);
        }
    }

    /**
     * 解压缩字节数组
     *
     * @param compressedData 压缩后的数据
     * @param originalLength 原始数据长度 (可选，但建议提供以提高安全性和性能)
     * @return 解压后的原始数据
     * @throws IOException 如果解压失败
     */
    public static byte[] decompress(byte[] compressedData, Integer originalLength) throws IOException {
        if (compressedData == null || compressedData.length == 0) {
            return compressedData;
        }
        try {
            long decompressedSize = originalLength != null ?
                    originalLength : Zstd.getFrameContentSize(compressedData);
            return Zstd.decompress(compressedData, (int) decompressedSize);
        } catch (Exception e) {
            throw new IOException("Zstd decompression failed", e);
        }
    }
}